# -*-coding:utf-8-*-

from bson.objectid import ObjectId
from daos.base_async import BaseDao, BaseRecord
import settings
from tornado.gen import coroutine, Return
import uuid
import hashlib


class App(BaseRecord):
    def __init__(self, data, key=None):
        #data.update(_id=data['app_id'], key=key)
        data.update(_id=data['app_id'])
        super(App, self).__init__(data)

    def __getattr__(self, item):
        if 'wx_mp_app_id' == item:
            args = ('weixin', 'mp', 'app_id')
        elif 'wx_mp_secret' == item:
            args = ('weixin', 'mp', 'secret')

        elif 'alipay_app_id' == item:
            args = ('alipay', 'appid')
        elif 'alipay_mch_id' == item:
            args = ('alipay', 'mch_id')
        elif 'alipay_rsa2_app_pri_key' == item:
            args = ('alipay', 'rsa2', 'app_pri')
        elif 'alipay_rsa2_app_pub_key' == item:
            args = ('alipay', 'rsa2', 'app_pub')
        elif 'alipay_rsa2_srv_pub_key' == item:
            args = ('alipay', 'rsa2', 'srv_pub')

        elif 'alipay_sandbox_app_id' == item:
            args = ('alipay_sandbox', 'appid')
        elif 'alipay_sandbox_mch_id' == item:
            args = ('alipay_sandbox', 'mch_id')
        elif 'alipay_sandbox_rsa2_app_pri_key' == item:
            args = ('alipay_sandbox', 'rsa2', 'app_pri')
        elif 'alipay_sandbox_rsa2_app_pub_key' == item:
            args = ('alipay_sandbox', 'rsa2', 'app_pub')
        elif 'alipay_sandbox_rsa2_srv_pub_key' == item:
            args = ('alipay_sandbox', 'rsa2', 'srv_pub')

        else:
            args = (item,)
        return self._get_optional_field(*args)

    def get_name(self):
        return self.data['name'].encode('utf-8')

    def get_wx_mp_app_id(self):
        return self.data['weixin']['mp']['app_id']

    def get_wx_mp_secret(self):
        return self.data['weixin']['mp']['secret']

    def get_wx_pay_mch_id(self):
        return self.data['weixin']['pay']['mch_id']

    def get_wx_pay_sign_key(self):
        if 'use_sandbox' in self.data['weixin']['pay'] and self.data['weixin']['pay']['use_sandbox']:
            return str(self.data['weixin']['pay']['sign_key_sandbox'])
        else:
            return str(self.data['weixin']['pay']['sign_key'])

    def is_wx_pay_use_sandbox(self):
        return self.data['weixin']['pay']['use_sandbox']

    def get_alidayu_app_key(self):
        return self.data['alidayu']['appkey']

    def get_alidayu_secret(self):
        return self.data['alidayu']['secret'].encode('utf-8')

    def get_alidayu_sms_sign_name(self):
        # 从数据库读出来的字符串使用 unicode 编码
        return self.data['alidayu']['sms']['sign_name'].encode('utf-8')

    def get_alidayu_sms_temp_code(self, for_what):
        return self.data['alidayu']['sms'][for_what]

    def get_permissions(self):
        if 'permissions' in self.data:
            return long(self.data['permissions'])
        else:
            return 0L

    def get_wx_pay_ca_certs(self):
        return self._get_optional_field('weixin', 'pay', 'ca_certs')

    def get_wx_pay_client_cert(self):
        return self._get_optional_field('weixin', 'pay', 'client_cert')

    def get_wx_pay_client_key(self):
        return self._get_optional_field('weixin', 'pay', 'client_key')

    # allinpay installment pay & transfer

    def get_allinpay_app_key(self):
        return self._get_optional_field('allinpay', 'app_key')

    def get_allinpay_mer_id(self):
        return self._get_optional_field('allinpay', 'mer_id')

    def get_allinpay_rsa_pub_key(self):
        return self._get_optional_field('allinpay', 'rsa_pub_key')

    def get_allinpay_rsa_pri_key(self):
        return self._get_optional_field('allinpay', 'rsa_pri_key')

    def get_allinpay_transfer_mer_id(self):
        return self._get_optional_field('allinpay', 'transfer', 'mer_id')

    def get_allinpay_transfer_rsa_pub_key(self):
        return self._get_optional_field('allinpay', 'transfer', 'rsa_pub_key')

    def get_allinpay_transfer_rsa_pri_key(self):
        return self._get_optional_field('allinpay', 'transfer', 'rsa_pri_key')

    def get_allinpay_transfer_username(self):
        return self._get_optional_field('allinpay', 'transfer', 'username')

    def get_allinpay_transfer_password(self):
        return self._get_optional_field('allinpay', 'transfer', 'password')


class AppsDao(BaseDao):
    def __init__(self, db):
        super(AppsDao, self).__init__(db, 'apps')

    def _get_record_object(self, data):
        return App(data)

    def _common_filter_cond(self, uid, aid):
        filter_cond = {'app_id': aid, '$or': [
            {'user_id': uid},
            {'developer_id': uid},
        ]}
        return filter_cond
    
    @coroutine
    def get_app(self, key, app_id=None, user_id=None):
        if app_id is None:
            filter_cond = {'appkey': key}
        else:
            filter_cond = self._common_filter_cond(user_id, app_id)
        projection = {'_id': 0}
        app = yield self.find_one(filter_cond, projection)
        if app is not None:
            app = App(app)
        raise Return(app)

    def _get_md5_hexdigest(self):
        return hashlib.md5(str(uuid.uuid4())).hexdigest().upper()

    @coroutine
    def create_app(self, user_id, data):            
        required_fields = ['name', 'desc']
        optional_fields = ['guest_enable', 'tags', 'developer_id', 'use_alipay_sandbox']
        data = self.extract_fields(data, required_fields=required_fields, optional_fields=optional_fields)
        if 'tags' in data and not isinstance(data['tags'], list):
            raise Exception('tags field must be list')
        if 'guest_enable' in data and not isinstance(data['guest_enable'], bool):
            raise Exception('guest_enable field must be bool')
        if 'use_alipay_sandbox' in data and not isinstance(data['use_alipay_sandbox'], bool):
            raise Exception('use_alipay_sandbox field must be bool')
        result = dict(app_id=self._get_md5_hexdigest(),
                      appkey=self._get_md5_hexdigest(),
                      secret=self._get_md5_hexdigest())
        data.update(user_id=user_id, guest_id=str(ObjectId()), **result)
        yield self.insert_one(data)
        raise Return(result)

    @coroutine
    def get_app_by_wx_mp_app_id(self, wx_mp_app_id):
        filter_cond = {'weixin.mp.app_id': wx_mp_app_id}
        projection = {'_id': 0}
        app = yield self.find_one(filter_cond, projection)
        if app is not None:
            app = App(app)
        raise Return(app)

    @coroutine
    def get_app_by_alipay_app_id(self, alipay_app_id):
        filter_cond = {'$or': [
            {'alipay.appid': alipay_app_id},
            {'alipay_sandbox.appid': alipay_app_id},
        ]}
        projection = {'_id': 0}
        app = yield self.find_one(filter_cond, projection)
        if app is not None:
            app = App(app)
        raise Return(app)

    @coroutine
    def delete_app(self, uid, aid):
        # 只有 app 的创建者才有删除权限
        filter_cond = {'app_id': aid, 'user_id': uid}
        result = yield self.delete_one(filter_cond)
        raise Return(1 == result.deleted_count)

    @coroutine
    def update_app(self, uid, aid, data):
        optional_fields = ['name', 'desc', 'guest_enable', 'tags', 'developer_id', 'use_alipay_sandbox']
        data = self.extract_fields(data, optional_fields=optional_fields)
        if 'tags' in data and not isinstance(data['tags'], list):
            raise Exception('tags field must be list')
        if 'guest_enable' in data and not isinstance(data['guest_enable'], bool):
            raise Exception('guest_enable field must be bool')
        if 'use_alipay_sandbox' in data and not isinstance(data['use_alipay_sandbox'], bool):
            raise Exception('use_alipay_sandbox field must be bool')
        if 0 == len(data):
            raise Return(False)
        filter_cond = self._common_filter_cond(uid, aid)
        result = yield self.update_one(filter_cond, {'$set': data})
        raise Return(1 == result.matched_count)

    @coroutine
    def update_app_aspect(self, uid, aid, aspect, data):
        if aspect not in ['weixin', 'alipay', 'alipay_sandbox', 'alidayu', 'key']:
            raise Exception('unsupported aspect')
        if 'key' == aspect:
            data = dict(appkey=self._get_md5_hexdigest(),
                        secret=self._get_md5_hexdigest())
            modification = data
        else:
            if 'weixin' == aspect:
                pass
            elif 'alipay' == aspect:
                pass
            elif 'alipay_sandbox' == aspect:
                pass
            elif 'alidayu' == aspect:
                pass
            modification = {aspect: data}
        filter_cond = self._common_filter_cond(uid, aid)
        result = yield self.update_one(filter_cond, {'$set': modification})
        raise Return(1 == result.matched_count)

    @coroutine
    def delete_app_aspect(self, uid, aid, aspect):
        if aspect not in ['weixin', 'alipay', 'alipay_sandbox', 'alidayu']:
            raise Exception('unsupported aspect')
        filter_cond = self._common_filter_cond(uid, aid)
        result = yield self.update_one(filter_cond, {'$unset': {aspect: 0}})
        raise Return(1 == result.matched_count)
